home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 026a / j1_xtab.zip / JP1_XTAB.PRG < prev   
Text File  |  1990-09-23  |  40KB  |  1,163 lines

  1. **   File:              JP1_XTAB.PRG
  2. **   Authors:           Bill Ramos and Kirk Nason, Ashton-Tate
  3. **   Modifications:     Jay Parsons, CIS 70160,340, A-T BBS Jparsons
  4. **   Date:              September 23, 1990
  5.  
  6. **   Description:       Crosstab function and related functions and
  7. **                      procedures from the CCBOOSTR.PRG file,
  8. **                      modified in two respects:
  9.  
  10. **                      1) Support for AVG, STD and VAR tabulations;
  11. **                      2) Columns and rows summarized.
  12.  
  13. **   Notes:             Two asterisks "**" introduce mods by Jay Parsons (JP).
  14. **                      All else is original code.
  15.  
  16. **                      This file does NOT contain all the functions,
  17. **                      procedures and macros required to run it.  It
  18. **                      replaces only the following items from CCBOOSTR.PRG:
  19.  
  20. **                      FUNCTION Crosstab
  21. **                      PROCEDURE XT_DisGets
  22. **                      PROCEDURE Com_XTab
  23.  
  24. **                      To use this file, copy CCBOOSTR.PRG to another file,
  25. **                      delete the named items only from it, append this file,
  26. **                      declare/compile it with SET PROC TO, and proceed.
  27.  
  28. **  Modifications Copyright (C) 1990, Jay Parsons.
  29.  
  30. **  License is granted to all persons to use these mods for personal purposes.
  31. **  License is granted to developers to distribute the compiled code as part
  32. **  of dBASE or RunTime custom applications, provided that the developer
  33. **  assumes all obligations of support.  Distribution of the source or compiled
  34. **  code of the modifications for any consideration, whether alone or as part
  35. ** of any other product, is otherwise prohibited.
  36.  
  37. **  No warranty of merchantability, fitness for any purpose or otherwise is
  38. **  made, and use of the modifications is at the user's sole risk.
  39.  
  40. **  The foregoing statements should not be understood to imply that the author
  41. **  of the modifications has title to the original code or any right to permit
  42. **  or regulate use or sale of the original code.
  43.                                        
  44. FUNCTION CrossTab   && UDS for Capturing Parameters for CrossTab
  45. *-----------------------------------------------------------------------
  46. * Capture CrossTab parameters for COM_XTAB
  47. *
  48. * Synopsis:
  49. * CROSSTAB( )
  50. * Description:
  51. * The CrossTab() function displays the front end to capture the 
  52. * parameters for the COM_XTAB (compute crosstab) procedure.
  53. * The CrossTab() function is called from within the QBE design
  54. * surface by placing the function CROSSTAB() in any one of the field
  55. * skeletons.  CrossTab() will not work within the file pothandle.
  56. * The user then fills in the optional title, the column to summarize
  57. * across the top, the column to summarize along the side, the column
  58. * to compute in the middle, and the type of operation (SUM,CNT,MAX,MIN).
  59. **  Operations AVG,STD and VAR added.  JP.
  60. * If the user presses F2 to perform the crosstab, the CrossTab() will
  61. * remove the CROSSTAB() reference from the skeleton and keyboard in
  62. * the commands to transfer to the Applications Panel in the Control
  63. * Center and run EXECCROS.
  64. * If the user presses Ctrl-End to save, but not execute, the CrossTab(), 
  65. * the CrossTab() function will save the parameters to the CROSSTAB.MEM 
  66. * file and clear out the CROSSTAB() reference from the skeletion.
  67. * If the user presses Escape to cancel, the CrossTab() will not save
  68. * any of the values and clear out the CROSSTAB() reference from 
  69. * the skeletion.
  70. * The summary field must be numeric.  The top and side fields can be
  71. * any type but memo.
  72. * See also:
  73. * EXECCROS.PRG    Run the CrossTab and display the results
  74. * COM_XTAB.PRG    Perform the actual CrossTab computations
  75. *-----------------------------------------------------------------------
  76.  
  77.   PRIVATE lc_talk, lc_cursor, lc_display, lc_status, lc_carry, lc_proc,;
  78.   ln_typeahd
  79.  
  80.   SAVE SCREEN TO qbe
  81.   IF SET("TALK") = "ON"
  82.     SET TALK OFF
  83.     lc_talk = "ON"
  84.   ELSE
  85.     lc_talk = "OFF"
  86.   ENDIF
  87.   lc_cursor = SET("CURSOR")
  88.   SET CURSOR ON
  89.  
  90.   lc_escape = SET("ESCAPE")
  91.   SET ESCAPE OFF
  92.   lc_status = SET("STATUS")
  93.   lc_safety = SET("SAFETY")
  94.   SET SAFETY OFF
  95.  
  96.   IF TYPE("kn_esc") = "U"
  97.     DO key_vars
  98.   ENDIF
  99.  
  100.   ll_iscolor = IIF( SET( "COLOR" ) = "ON", .t., .f. )
  101.   IF TYPE("SAYCOLOR") = "U"
  102.     PUBLIC saycolor
  103.     IF ll_iscolor
  104.       saycolor = "rg+"
  105.     ELSE
  106.       saycolor = "w/n"
  107.     ENDIF
  108.   ENDIF
  109.   IF TYPE("FILLCOLOR") = "U"
  110.     PUBLIC fillcolor
  111.     IF ll_iscolor
  112.       fillcolor = "gb+"
  113.     ELSE
  114.       fillcolor = "n"
  115.     ENDIF
  116.   ENDIF
  117.   IF TYPE("MESSCOLOR") = "U"
  118.     PUBLIC messcolor
  119.     messcolor = COLOR("MESSAGE")
  120.     ln_slash = AT("/", messcolor)
  121.     ln_plus  = AT("+", messcolor)
  122.     IF ln_plus = 0
  123.       messcolor = "+" + messcolor
  124.     ELSE
  125.       IF ln_plus > ln_slash
  126.         messcolor = "+" + messcolor
  127.       ENDIF
  128.     ENDIF
  129.   ENDIF
  130.  
  131.   DEFINE WINDOW Crosstab FROM 5,7 TO 18,73 DOUBLE
  132.   DO shadowg WITH 5,7,18,73
  133.   ACTIVATE WINDOW Crosstab
  134.  
  135.   DO XT_DisSays
  136.  
  137.   DO XT_IniVars
  138.  
  139. *-- Check to see if a numeric field is present in the DBF
  140.   ll_numpres = .F.
  141.   ln_fldcnt = 1
  142.   DO WHILE .NOT. EMPTY( FIELD( ln_fldcnt ) )
  143.     IF TYPE( FIELD( ln_fldcnt ) ) $ "NF"
  144.       ll_numpres = .T.
  145.       EXIT
  146.     ENDIF
  147.     ln_fldcnt = ln_fldcnt + 1
  148.   ENDDO
  149.   IF ll_numpres                        && Numeric field found
  150. **  Next line replaced with expanded options. JP
  151. **  gc_actlist = "@M SUM,CNT,MAX,MIN"  && Give the full set of options
  152.     gc_actlist = "@M SUM,CNT,MAX,MIN,AVG,STD,VAR"  && Give full set of options
  153.   ELSE
  154.     gc_actlist = "@M CNT,MAX,MIN"      && Else allow all but SUM
  155.   ENDIF
  156.  
  157.   ON KEY LABEL f2       DO XT_F2Exit
  158.   ON KEY LABEL shift-f1 DO XT_FldPick  WITH VARREAD()
  159.   ll_escape = .F.
  160.   ll_edit = .T.
  161.   ll_f2 = .F.
  162.   DO WHILE ll_edit
  163.     DO XT_DisGets
  164.     READ
  165.     ln_readkey = READKEY()
  166.     DO CASE
  167.       CASE ln_readkey = rn_Esc
  168.         ll_escape = .T.
  169.         ll_edit = .F.
  170.       CASE ln_readkey = rn_CtrlEnd
  171.         IF EMPTY( gc_xtop ) .OR. EMPTY( gc_xside ) .OR. EMPTY( gc_xsum )
  172.           ll_edit = .T.
  173.           ACTIVATE SCREEN
  174.           @ 24,0
  175.           DO Beep
  176.           lc_msg = "The top, side, and summary fields must be specified" + ;
  177.                    " (Press SPACE)"
  178.           @ 24, Center( lc_msg,80 ) SAY lc_msg COLOR &messcolor
  179.           ln_tmp = INKEY(0)
  180.           @ 24,0
  181.           ACTIVATE WINDOW CrossTab
  182.         ELSE
  183.           ll_edit = .F.
  184.         ENDIF
  185.     ENDCASE
  186.   ENDDO
  187.   ON KEY LABEL f2
  188.   ON KEY LABEL shift-f1
  189.  
  190.   DO XT_TrmVars                  && Trim the vars from the form
  191.   lc_fullp = SET("FULLPATH")
  192.   SET FULLPATH ON
  193.  
  194.   gl_XIsCC = XT_MakExt()        && Make EXECCROS.PRG is needed
  195.   IF ll_f2
  196.     gc_xdbf = DBF()             && Capture open DBF file
  197.     DO XT_TrmVars                && Trim the vars from the form
  198.     SAVE TO crosstab ALL LIKE gc_x*
  199.     SET INSTRUCT OFF            && Prevent extra message when running app
  200.     SET SAFETY OFF
  201.  
  202.     IF FILE( "crosstab.qbe" )   && Delete the existing CROSSTAB.QBE file
  203.       ERASE crosstab.qbe
  204.     ENDIF
  205.  
  206.     RESTORE MACROS FROM ccboostr
  207.     PLAY MACRO SaveCros
  208. *-- Keystrokes in SaveCros macro file
  209. *     {Ctrl-y}{Enter}           && Clear Crosstab() out of skeleton
  210. *     {Alt-l}s                  && Layout menu, Save bar
  211. *     {Home}{Ctrl-y}            && Clear existing QBE name
  212. *     crosstab{Enter}           && Save as Crosstab.qbe
  213. *     {Ctrl-End}                && Exit QBE
  214. *     {rightarrow}{rightarrow}  && Cursor thru CC to Application panel
  215. *     {rightarrow}
  216. *     {rightarrow}
  217. *     execcros{Enter}           && Run ExecCros.PRG
  218. *     y                         && Confirm execution
  219. *-- End of keystroke macro file
  220.  
  221.   ELSE
  222.     IF .NOT. ll_escape
  223.       ll_edit = .F.
  224.       gc_xdbf = DBF()
  225.       DO XT_TrmVars              && Trim the vars from the form
  226.       SAVE TO crosstab ALL LIKE gc_x*
  227.     ENDIF
  228.     KEYBOARD CHR(kn_CtrlY)      && Remove the crosstab()
  229.     KEYBOARD CHR(kn_Enter)      && Restore skeleton for field
  230.   ENDIF
  231.  
  232.   DEACTIVATE WINDOW Crosstab
  233.   RELEASE WINDOW Crosstab
  234.   RELEASE POPUP crosstab
  235.  
  236.   RELEASE gc_xtitl,gc_xtop,gc_xside,gc_xsum,gc_xoper
  237.   RELEASE aa_lookup, params
  238.  
  239.   SET STATUS &lc_status.
  240.   SET CURSOR &lc_cursor.
  241.   SET ESCAPE &lc_escape.
  242.   SET SAFETY &lc_safety.
  243.   SET TALK &lc_talk.
  244.   SET FULLPATH &lc_fullp
  245.  
  246.   RESTORE SCREEN FROM qbe
  247.   RELEASE SCREEN qbe
  248. RETURN("")
  249. *-- EOF: CrossTab()
  250.  
  251. PROCEDURE XT_DisGets && UDS - CrossTab()
  252. *--------------------------------------------------------------------
  253. * Display the GET fields for the form.  Display the Navigation line
  254. * before starting the GET processing.  Allow Shift-F1 picklists
  255. * for the field selections.
  256. *--------------------------------------------------------------------
  257.   PRIVATE lc_errmsg
  258.   lc_errmsg = "Try again"
  259.  
  260. *-- Perform the GETs
  261.   @  2, 17 GET gc_xtitl  PICTURE "@S40";
  262.     MESSAGE "Specify an optional heading for the CrossTab display";
  263.     WHEN XT_NavDsp( 1 )
  264.  
  265.   @  4, 34 GET gc_xtop   PICTURE "@!S10" ;
  266.     MESSAGE "Specify the field to summarize along the top of the CrossTab";
  267.     WHEN XT_NavDsp( 2 );
  268.     VALID REQUIRED IsInView( gc_xtop ) .AND. TYPE( gc_xtop )<>"M";
  269.     ERROR IIF( TYPE(gc_xtop)="M",;
  270.                   "Memo fields are not allowed with CrossTab",;
  271.                   "The field specified is not in the current view")
  272.  
  273.  
  274.   @  9,  0 GET gc_xside  PICTURE "@!S10" ;
  275.     MESSAGE "Specify the field to summarize along the side of the CrossTab";
  276.     WHEN XT_NavDsp( 3 );
  277.     VALID REQUIRED IsInView( gc_xside ) .AND. TYPE( gc_xside )<>"M";
  278.     ERROR IIF( TYPE(gc_xside)="M",;
  279.                   "Memo fields are not allowed with CrossTab",;
  280.                   "The field specified is not in the current view")
  281.  
  282.   @  8, 34 GET gc_xsum   PICTURE "@!S10" ;
  283.     MESSAGE "Specify the field to operate on in the body of the CrossTab";
  284.     WHEN XT_NavDsp( 4 );
  285.     VALID REQUIRED IsInView( gc_xsum ) .AND. TYPE( gc_xsum )<>"M";
  286.     ERROR IIF( TYPE(gc_xsum)="M",;
  287.                   "Memo fields are not allowed with CrossTab",;
  288.                   "The field specified is not in the current view")
  289.  
  290. ** Error message below changed to reflect new options.  JP
  291.  
  292.   @ 10, 39 GET gc_xoper  PICTURE ( gc_actlist ) ;
  293.     MESSAGE "Specify the operation on the summary field";
  294.     WHEN XT_NavDsp( 5 ) ;
  295.     VALID REQUIRED ( TYPE( gc_xsum ) = "C" .AND. ;
  296.                      gc_xoper <> "SUM"             ) .OR. ;
  297.                    ( TYPE( gc_xsum ) = "L" .AND. ;
  298.                      gc_xoper = "CNT" ) .OR.;
  299.                      TYPE( gc_xsum ) $ "NFD";
  300.     ERROR IIF( TYPE( gc_xsum ) = "C", ;
  301.                   "SUM, AVG, STD and VAR require numeric fields", ;
  302.                   "while CNT alone may be used with logical fields")
  303. **                "SUM can not be used with character fields", ;
  304. **                "MAX, MIN, or SUM can not be used with logical fields")
  305.  
  306. RETURN
  307. *-- EOP: XT_DisGets
  308.  
  309. PROCEDURE Com_XTab  && Compute CrossTab Based on Parameters
  310. PARAMETERS p_topfld, p_sidfld, p_calfld, p_calc
  311. *------------------------------------------------------------------
  312. * Compute data normalized form of a relational database 
  313. * file to a spreadsheet-like structure.  It summarizes the 
  314. * data in one field by expressing it in terms of two other 
  315. * fields.
  316. * SYNTAX 
  317. * DO Com_XTab WITH <top field name>, <side field name>, ;
  318. *                  <calc field>, <type calc>
  319. * where <type calc> can be one of the following functions:
  320. *   CNT
  321. *   MAX
  322. *   MIN
  323. *   SUM
  324. **      Functions added by JP
  325. **  AVG        && average
  326. **  STD        && population standard deviation
  327. **  VAR        && population variance
  328. * USAGE
  329. * The Com_XTab command handles all records in the active 
  330. * file that match any filter condition placed on the data.
  331. * Com_XTab places the result of the CROSSTAB in a file
  332. * named CROSSTAB.  Com_XTab will also place the column names
  333. * for the <top field name> in a file named COLUMNS.  
  334. * Com_XTab will overwrite these files.
  335. **
  336. **  Comments added by JP
  337. **
  338. **  When using AVG, STD or VAR, the procedure adds a file,
  339. **  N_XTAB.DBF, to hold the number of occurrences of the same
  340. **  combination of top and side fields.
  341. **
  342. **  When using STD or VAR, the procedure adds a third file,
  343. **  SQ_XTAB.DBF, to hold the sum of the squares of the data.
  344. **
  345. **  Com_XTab will overwrite these files too.
  346. * OPTIONS
  347. * CNT counts the records that match the <top field name>
  348. * and the <side field name>.
  349. * SUM adds together the values of <calc field> that match
  350. * the <top field name> and the <side field name>.
  351. * MAX determines the maximum value of <calc field> for
  352. * matching values of <top field name> and <side field name>.
  353. * MIN determines the minimum value of <calc field> for
  354. * matching values of <top field name> and <side field name>.
  355. ** Comments added by JP
  356. **
  357. ** AVG averages the values of <calc field> that match the values
  358. ** of <top field name> and <side field name>.
  359. **
  360. ** STD calculates the population standard deviation of the values
  361. ** of <calc field> that match the <top field name> and <side field
  362. ** name>, the square root of VAR.
  363. **
  364. ** VAR calculates the population variance of the values of <calc field>
  365. ** that match the <top field name> and <side field name>.
  366. **
  367. ** If there are no occurrences, the AVG,STD and VAR are (meaninglessly)
  368. ** reported as zero to save the time of creating a file with
  369. ** character fields, copying the real averages to it as strings
  370. ** and filling other fields with blanks.  Zero-occurrence fields
  371. ** are not counted in the summary totals.
  372. **
  373. * EXAMPLE
  374. * Consider the following database file.
  375. * CAMPING   Name  Item     Price
  376. * -------   ----  -----   ------
  377. *     1     John  Tent    245.00
  378. *     2     Mark  Stove    59.00
  379. *     3     Jane  Heater   75.00
  380. *     4     Mark  Stove    79.00
  381. *     5     Jane  Heater   95.00
  382. *     6     Jane  Tent    325.00
  383. *     7     John  Tent    175.00
  384. *     8     Mark  Stove    89.00
  385. * DO Com_XTab WITH "Item", "Name", "Price", "SUM"
  386. *   will create the following two files:
  387. * CROSSTAB  Name   COL01   COL02   COL03
  388. * --------  ----  ------  ------  ------
  389. *     1     Jane  170.00    0.00  325.00  
  390. *     2     John    0.00    0.00  420.00
  391. *     3     Mark    0.00  227.00    0.00
  392. * The CROSSTAB file will be active in work area 1.
  393. *
  394. * COLUMNS   Column  TopField
  395. * -------   ------  --------
  396. *     1     COL01   Heater
  397. *     2     COL02   Stove
  398. *     3     COL03   Tent
  399. * The COLUMNS file will be open in work area 2.  The COLUMNS
  400. * file will have an MDX TAG Column on the Column field.  
  401. **
  402. ** Comments by JP
  403. ** As modified, the Crosstab file will also contain a field and a
  404. ** record summarizing the data in that row and column, and another
  405. ** undisplayed field "SEQ" used to keep it in order for display.
  406. ** The N_XTab aand SQ_XTab files, if created, share the structure of
  407. ** Crosstab and map directly to it by row and column; they basically
  408. ** add a third dimension to hold values needed for their calculations.
  409. **
  410. * COM_XTAB will create a SET FIELDS TO command for each column
  411. * based on the Columns file to get the correct column heading.
  412. * This technique will only work if the resulting column name
  413. * is less than or equal to 10 characters and it contains
  414. * no punctuation or spaces.
  415. * You may use this file in the report designer to place a 
  416. * meaningfull column label for a report.  Before entering
  417. * the report designer execute the command:
  418. *     SET FIELDS TO <Enter>
  419. * SEE ALSO
  420. *------------------------------------------------------------------
  421.  
  422. *-- Save the current environment
  423.   SET FILTER TO                 && Clear the filter condition if any
  424.   PRIVATE lc_talk
  425.   IF SET("TALK") = "ON"         && Set talk off, but restore it later
  426.     SET TALK OFF
  427.     lc_talk = "ON"
  428.   ELSE
  429.     lc_talk = "OFF"
  430.   ENDIF
  431.  
  432.   PRIVATE lc_safety
  433.   lc_safety = SET("SAFETY")     && Save the SAFETY state
  434.   SET SAFETY OFF                && and turn it off.
  435.  
  436.   PRIVATE lc_excl
  437.   lc_excl = SET("EXCLUSIVE")    && Save the EXCLUSIVE state
  438.   SET EXCLUSIVE ON              && and turn it on.
  439.  
  440.   PRIVATE lc_status
  441.   lc_status = SET("STATUS")     && Save the status bar before turning it off
  442.   SET STATUS OFF
  443.  
  444.   SET CURSOR OFF
  445.   lc_stat1 = "Determining the number of UNIQUE Records"
  446.   lc_stat2 = "Creating CROSSTAB and COLUMNS DBF files "
  447.   lc_stat3 = "Populating the COLUMNS file             "
  448.   lc_stat4 = "Populating the CROSSTAB file            "
  449.   lc_stat5 = "Creating Pseudo Field Headings          "
  450.   DEFINE WINDOW com_xtab FROM 5,9 TO 13, 70
  451.   DO ShadowG WITH 5, 9, 13, 70
  452.   ACTIVATE WINDOW com_xtab
  453.  
  454.   @ 0, 20 SAY "Computing CrossTab"
  455.   @ 2, 5 SAY lc_stat1 + + CHR(205) + CHR(205) + CHR(16)
  456.   @ 2, 50 SAY "Working"
  457.  
  458. *-- Make sure a data file is open
  459.   IF Empty( ALIAS() )
  460.     lc_error = XT_ErrMsg( "No file is in use!", "" )
  461.     DO XT_RestEv
  462.     CANCEL
  463.   ENDIF   
  464.  
  465. *-- Preserve the order of the file going into crosstab
  466.   lc_order = ORDER()
  467.  
  468. ** Following 3 routines mostly commented out and replaced by JP due to
  469. ** surpassing ugliness of original code.
  470. ** Yeah, I know, these guys worked hard and I shouldn't knock it if it
  471. ** works.  But sometimes, I can't resist.
  472.  
  473. *-- Strip the alias from the field if it exists
  474.    ln_apos   = AT( "->", p_topfld )  && Alias position, optional
  475. *  IF ln_apos > 0                    && Alias exists, now get the field
  476. *    lc_ptopfld = SUBSTR( p_topfld, ln_apos + 2, LEN( p_topfld ) - ln_apos )
  477. *  ELSE
  478. *    lc_ptopfld = UPPER( p_topfld )
  479. *  ENDIF
  480.    lc_ptopfld = upper(iif(ln_apos=0,p_topfld,substr(p_topfld,ln_apos+2)))
  481.  
  482.    ln_apos   = AT( "->", p_sidfld )  && Alias position, optional
  483. *  IF ln_apos > 0                    && Alias exists, now get the field
  484. *    lc_psidfld = SUBSTR( p_sidfld, ln_apos + 2, LEN( p_sidfld ) - ln_apos )
  485. *  ELSE
  486. *    lc_psidfld = UPPER( p_sidfld )
  487. *  ENDIF
  488.    lc_psidfld = upper(iif(ln_apos=0,p_sidfld,substr(p_sidfld,ln_apos+2)))
  489.  
  490.    ln_apos   = AT( "->", p_calfld )  && Alias position, optional
  491. *  IF ln_apos > 0                    && Alias exists, now get the field
  492. *    lc_pcalfld = SUBSTR( p_calfld, ln_apos + 2, LEN( p_calfld ) - ln_apos )
  493. *  ELSE
  494. *    lc_pcalfld = UPPER( p_calfld )
  495. *  ENDIF
  496.    lc_pcalfld = upper(iif(ln_apos=0,p_calfld,substr(p_calfld,ln_apos+2)))
  497.  
  498. *-- Make sure the fields exist in the current file
  499. *-- Already checked in CrossTab()
  500. *-----------------------------------------------------------------------
  501. NOTE   IF .NOT. XT_isfld( lc_ptopfld )
  502. NOTE     lc_error = XT_ErrMsg( "Top field: " + lc_ptopfld + ;
  503. NOTE                          " is not in the file", "" )
  504. NOTE     DO XT_RestEv
  505. NOTE     RETURN
  506. NOTE   ENDIF
  507. NOTE 
  508. NOTE   IF .NOT. XT_isfld( lc_psidfld )
  509. NOTE     lc_error = XT_ErrMsg( "Side field: " + lc_psidfld + ;
  510. NOTE                          " is not in the file", "" )
  511. NOTE     DO XT_RestEv
  512. NOTE     RETURN
  513. NOTE   ENDIF
  514. NOTE 
  515. NOTE   IF .NOT. XT_isfld( lc_pcalfld )
  516. NOTE     lc_error = XT_ErrMsg( "Calculated field: " + lc_pcalfld + ;
  517. NOTE                          " is not in the file", "" )
  518. NOTE     DO XT_RestEv
  519. NOTE     RETURN
  520. NOTE   ENDIF
  521. *-----------------------------------------------------------------------
  522.  
  523. *-- Verify the calculation type
  524. ** Types added by JP.  Hey!  How did AVG get in the original?
  525. * IF .NOT. p_calc $ "SUM,CNT,MAX,MIN,AVG"
  526.   IF .NOT. p_calc $ "SUM,CNT,MAX,MIN,AVG,STD,VAR"
  527.     lc_error = XT_ErrMsg( "Calculation type: " + p_calc + ;
  528.                          " is not supported!", "" )
  529.     DO XT_RestEv
  530.     CANCEL
  531.   ENDIF
  532.  
  533. *-- Compute the number of top and side values
  534.   lc_typetop = TYPE( lc_ptopfld )
  535.   DO CASE
  536.     CASE lc_typetop = "L"
  537.       INDEX ON IIF( &lc_ptopfld., "T", "F" ) TAG topfld UNIQUE
  538.     CASE lc_typetop = "C"
  539.       INDEX ON UPPER( &lc_ptopfld. ) TAG topfld UNIQUE
  540.     CASE lc_typetop $ "NF"
  541.       INDEX ON &lc_ptopfld. TAG topfld UNIQUE
  542.     CASE lc_typetop = "D"
  543.       INDEX ON DTOS( &lc_ptopfld. ) TAG topfld UNIQUE
  544.   ENDCASE
  545.  
  546.   COUNT TO ln_columns
  547.   IF ln_columns > 254
  548.     lc_error = XT_ErrMsg( "Unique columns for the top exceeds 254", "")
  549.     DELETE TAG topfld
  550.     DO XT_RestEv
  551.     CANCEL
  552.   ENDIF
  553.  
  554.   lc_typesid = TYPE( lc_psidfld )
  555.   DO CASE
  556.     CASE lc_typesid = "L"
  557.       INDEX ON IIF( &lc_psidfld., "T", "F" ) TAG sidfld UNIQUE
  558.     CASE lc_typesid = "C"
  559.       INDEX ON UPPER( &lc_psidfld. ) TAG sidfld UNIQUE
  560.     CASE lc_typesid $ "NF"
  561.       INDEX ON &lc_psidfld. TAG sidfld UNIQUE
  562.     CASE lc_typesid = "D"
  563.       INDEX ON DTOS( &lc_psidfld. ) TAG sidfld UNIQUE
  564.   ENDCASE
  565.  
  566.   COUNT TO ln_rows
  567.  
  568.   @ 2, 50 SAY "Complete"
  569.   @ 3, 5 SAY lc_stat2 + + CHR(205) + CHR(205) + CHR(16)
  570.   @ 3, 50 SAY "Working"
  571.  
  572. *-- Create the CROSSTAB file to hold the values
  573.   COPY TO _cross STRUCTURE EXTENDED
  574.   SELECT SELECT()
  575.   USE _cross        
  576.  
  577. *-- Make the calc field's attributes global for use in the SET FIELDS TO
  578. *-- command after the crosstab is created to adjust the column size.
  579.   IF TYPE( "gn_dec" ) <> "U"
  580.     RELEASE gn_dec
  581.   ENDIF
  582.   PUBLIC gn_dec
  583.   IF TYPE( "gn_len" ) <> "U"
  584.     RELEASE gn_len
  585.   ENDIF
  586.   PUBLIC gn_len
  587.  
  588. *-- Get the attributes for the side field
  589.   LOCATE FOR field_name = UPPER( lc_psidfld )
  590.   ln_lensid = field_len
  591.   ln_decsid = field_dec
  592.  
  593. *-- Get the attributes for the top field
  594.   LOCATE FOR field_name = UPPER( lc_ptopfld )
  595.   ln_lentop = field_len
  596.   ln_dectop = field_dec
  597.  
  598. *-- Get the attributes for the calculated column
  599.   LOCATE FOR field_name = UPPER( lc_pcalfld )
  600.   IF p_calc = "CNT"
  601.     gn_len = LEN( ALLTRIM( RECCOUNT() ) )
  602.     gn_dec = 0
  603.   ELSE
  604.     gn_len = field_len
  605.     IF p_calc = "SUM" .AND. gn_len < 16   && Adjust column length if SUM
  606.       gn_len = gn_len + 2
  607.     ENDIF
  608.     gn_dec = field_dec
  609.   ENDIF
  610.   ZAP
  611.  
  612. *----------------------------------------------------------------------
  613. *-- Create the CROSSTAB file fields
  614. *----------------------------------------------------------------------
  615. *-- Make the Side Column column
  616.   APPEND BLANK
  617. **  Changed to type "C", length minimum 7 - easier to deal with labels JP
  618.   REPLACE field_name WITH lc_psidfld, ;
  619.           field_type WITH "C", ;
  620.           field_len  WITH max( ln_lensid, 7 ) , ;
  621.           field_dec  WITH 0 , ;
  622.           field_idx  WITH "N"
  623. *         field_type WITH IIF( lc_typesid = "L", "C", lc_typesid ), ;
  624. *         field_len  WITH ln_lensid) , ;
  625. *         field_dec  WITH ln_decsid ,  ;
  626.  
  627. *-- Make the crosstab columns
  628.   ln_col = 1                      && Counter for creating columns
  629.   DO WHILE ln_col <= ln_columns
  630.     lc_colname = "COL" + SUBSTR( STR( 100 + ln_col, 3, 0), 2, 2 )
  631.     APPEND BLANK
  632.     REPLACE field_name WITH lc_colname, ;
  633.             field_type WITH "N"       , ;
  634.             field_len  WITH gn_len    , ;
  635.             field_dec  WITH gn_dec    , ;
  636.             field_idx  WITH "N"
  637.  
  638.     ln_col = ln_col + 1           && Increment column counter
  639.   ENDDO
  640.  
  641. **-- Make the summary column.  Added by JP
  642. **   If you don't want summary column, take this section out.
  643.   APPEND BLANK
  644.   lc_rtcol = "Z" + replicate( "_", gn_len - 1 ) && last possible name
  645.   REPLACE field_name WITH lc_rtcol, ;
  646.           field_type WITH "N"  , ;
  647.           field_len  WITH gn_len,;
  648.           field_dec  WITH gn_dec,;
  649.           field_idx  WITH "N"
  650.  
  651. **  Sequence field added to enable renaming bottom record without
  652. **  index causing it to move JP - needed only for summaries
  653.    APPEND BLANK
  654.    REPLACE field_name WITH "Seq", ;
  655.           field_type WITH "N",    ;
  656.           field_len  WITH int( log10( ln_rows + 1 ) ) + 1,;
  657.           field_dec  WITH 0,      ;
  658.           field_idx  WITH "N"
  659.  
  660.    IF p_calc $ "STD,VAR"
  661.      COPY TO _cross2
  662.    ENDIF
  663.  
  664.  ** back to original code
  665.  
  666.   CREATE crosstab FROM _cross
  667.   USE crosstab ALIAS crosstab
  668.  
  669.   INDEX ON UPPER( &lc_psidfld ) TAG &lc_psidfld
  670.  
  671. *----------------------------------------------------------------------
  672. *-- Make the COLUMN file fields
  673. *----------------------------------------------------------------------
  674.   SELECT SELECT()
  675.   USE _cross
  676.   ZAP
  677.   APPEND BLANK
  678.   REPLACE field_name WITH "COLUMN", ;
  679.           field_type WITH "C"     , ;
  680.           field_len  WITH 5       , ;
  681.           field_dec  WITH 0       , ;
  682.           field_idx  WITH "Y"
  683.   APPEND BLANK            
  684.   REPLACE field_name WITH lc_ptopfld, ;
  685.           field_type WITH "C"       , ;
  686.           field_len  WITH ln_lentop , ;
  687.           field_dec  WITH 0         , ;
  688.           field_idx  WITH "N"
  689.                        
  690.   CREATE columns FROM _cross
  691.   USE columns ALIAS columns
  692.   INDEX ON UPPER( &lc_ptopfld ) TAG &lc_ptopfld
  693.  
  694.   @ 3, 50 SAY "Complete"
  695.   @ 4, 5 SAY lc_stat3 + + CHR(205) + CHR(205) + CHR(16)
  696.   @ 4, 50 SAY "Working"
  697.  
  698. *-- Fill in the column file with the UNIQUE values for the top fields
  699. *-- from the source DBF
  700.   SELECT 1
  701.   SET ORDER TO TAG topfld
  702.   ln_col = 1
  703.   SCAN      
  704.     lc_colname =  "COL" + SUBSTR( STR( 100 + ln_col, 3, 0), 2, 2 )
  705.     SELECT columns
  706.     APPEND BLANK
  707.     DO CASE 
  708.       CASE lc_typetop = "C"
  709.         REPLACE column      WITH lc_colname, ;
  710.                 &lc_ptopfld WITH &p_topfld
  711.       CASE lc_typetop = "L"
  712.         REPLACE column      WITH lc_colname, ;
  713.                 &lc_ptopfld WITH IIF( &p_topfld., "T", "F" )
  714.       CASE lc_typetop $ "NF"
  715.         REPLACE column      WITH lc_colname, ;
  716.                 &lc_ptopfld WITH ;
  717.                   ALLTRIM( STR( &p_topfld, ln_lentop, ln_dectop ) )
  718.       CASE lc_typetop = "D"
  719.         REPLACE column      WITH lc_colname, ;
  720.                 &lc_ptopfld WITH DTOC( &p_topfld. )
  721.     ENDCASE
  722.  
  723.     ln_col = ln_col + 1
  724.     SELECT 1
  725.   ENDSCAN
  726.                                                          
  727. *-- Initialize the crosstab matrix by filling in the side column
  728. *-- with the UNIQUE values from the source DBF
  729. *-- This two pass approach is no faster than filling it in as
  730. *-- the crosstab function is populated.  It has the added advantage
  731. *-- of correctly ordering the side column.
  732.   SELECT 1
  733.   SET ORDER TO TAG sidfld
  734. ** line added for indexing value
  735.   ln_ordno = 1
  736.   SCAN
  737.     SELECT crosstab
  738.     APPEND BLANK
  739. ** Type changed to character for use with summaries JP
  740.     DO CASE
  741.       CASE lc_typesid = "C"
  742.         lc_colname = &p_sidfld
  743.       CASE lc_typesid = "L"
  744.         lc_colname = iif( &p_sidfld., "T", "F" )
  745.       CASE lc_typesid $ "NF"
  746.         lc_colname = str( &p_sidfld., ln_lensid, ln_decsid )
  747.       CASE lc_typesid = "D"
  748.         lc_colname = dtos( &p_sidfld )        && we want date order
  749.      ENDCASE
  750.      REPLACE &lc_psidfld WITH lc_colname
  751. *    REPLACE &lc_psidfld WITH ;
  752. *            IIF(lc_typesid = "L", ;
  753. *                IIF( &p_sidfld, "T", "F" ),;
  754. *                &p_sidfld ;
  755. *            )
  756. ** Also add sequential number for use with summaries
  757.      REPLACE Seq WITH ln_ordno
  758.      ln_ordno = ln_ordno + 1
  759. ** end this change
  760.  
  761.     SELECT 1
  762.   ENDSCAN
  763.  
  764. ** add a blank record for summary row, last in order JP
  765. ** again, this affects only addition of summary row & col
  766.   SELECT crosstab
  767.   lc_botrow = "Z" + replicate( "_", ln_lensid - 1 )
  768.   APPEND BLANK
  769.   REPLACE &lc_psidfld WITH lc_botrow, Seq WITH ln_ordno
  770.  
  771. ** Added by JP -- make auxiliary files like Crosstab, in same order
  772. ** Needed for new operations, regardless of summaries in or out
  773.   IF p_calc $ "AVG,STD,VAR"
  774.     SET ORDER TO
  775.     COPY TO N_XTAB
  776.     USE N_XTAB ALIAS Number IN select()
  777.     INDEX ON UPPER( &lc_psidfld ) TAG &lc_psidfld
  778.     SET ORDER TO TAG &lc_psidfld
  779.     IF p_calc # "AVG"
  780. ** file for squares needs longer fields to hold possible digits
  781.       SELECT crosstab
  782.       USE _cross2
  783.       GOTO 2
  784.       SCAN WHILE recno() < reccount() - 1
  785.         REPLACE field_len WITH min( gn_len * gn_len, 20 )
  786.       ENDSCAN
  787.       GO reccount() - 1
  788.       REPLACE field_len WITH min( gn_len * gn_len + 2, 20 )
  789.       CREATE SQ_XTAB FROM _cross2
  790.       APPEND FROM crosstab
  791.       USE
  792.       USE SQ_XTAB ALIAS Squares
  793.       INDEX ON UPPER( &lc_psidfld ) TAG &lc_psidfld
  794.       SET ORDER TO TAG &lc_psidfld
  795.       USE crosstab ALIAS crosstab IN select()
  796.     ENDIF
  797.     SELECT crosstab
  798.     SET ORDER TO TAG &lc_psidfld
  799.     SELECT 1
  800.   ENDIF
  801. ** end of additions by JP
  802.  
  803.   @ 4, 50 SAY "Complete"
  804.   @ 5, 5 SAY lc_stat4 + + CHR(205) + CHR(205) + CHR(16)
  805.   @ 5, 50 SAY "Working"
  806.  
  807. *-- Compute the matrix - brute force
  808.   SELECT 1
  809.   SET ORDER TO &lc_order          && Return to the original order
  810. *                                 && in the event a FOR condition was
  811. *                                 && placed on the index.
  812.                                                                
  813.   SET FILTER TO &gc_filter
  814.   SCAN
  815. **  Type changed to character JP
  816.     DO CASE
  817.       CASE lc_typesid = "C"
  818.         l_seekval = upper( &lc_psidfld )
  819.       CASE lc_typesid = "L"
  820.         l_seekval = IIF( &lc_psidfld., "T", "F" )
  821.       CASE lc_typesid $ "NF"
  822.         l_seekval = str( &lc_psidfld., ln_lensid, ln_decsid )
  823.       CASE lc_typesid = "D"
  824.         l_seekval = dtos( &p_sidfld )
  825.      ENDCASE
  826.  
  827. *   IF lc_typesid = "L"
  828. *     l_seekval = IIF( &lc_psidfld., "T", "F" )
  829. *   ELSE
  830. *     IF lc_typesid = "C"
  831. *       l_seekval = UPPER( &lc_psidfld )
  832. *     ELSE
  833. *       l_seekval = &lc_psidfld
  834. *     ENDIF
  835. *   ENDIF
  836. ** end this change
  837.  
  838.     IF SEEK( l_seekval, "crosstab" )
  839.  
  840.       DO CASE
  841.         CASE lc_typetop = "C"
  842.           l_seekval = UPPER( &lc_ptopfld )
  843.         CASE lc_typetop = "L"
  844.           l_seekval = IIF( &lc_ptopfld., "T", "F" )
  845.         CASE lc_typetop $ "NF"
  846.           l_seekval = ALLTRIM( STR( &lc_ptopfld, ln_lentop, ln_dectop ) )
  847.         CASE lc_typetop = "D"
  848.           l_seekval = DTOC( &lc_ptopfld )
  849.       ENDCASE
  850.  
  851.       IF SEEK( l_seekval, "columns" )
  852.         lc_colname = columns->column
  853.         DO CASE
  854. ** Changed by JP, new operations
  855. *         CASE p_calc = "SUM"
  856.           CASE p_calc $ "SUM,AVG,STD,VAR"
  857. ** end change
  858.             REPLACE crosstab->&lc_colname WITH ;
  859.                     &p_calfld + crosstab->&lc_colname
  860. ** Added by JP, ditto
  861.             IF p_calc # "SUM"
  862.               SELECT Number
  863.               GO recno("crosstab")
  864.               REPLACE &lc_colname WITH &lc_colname + 1
  865.               IF p_calc $ "STD,VAR"
  866.                 SELECT Squares
  867.                 GO recno("crosstab")
  868.                 REPLACE &lc_colname WITH &lc_colname + ;
  869.                   &p_calfld * &p_calfld
  870.               ENDIF STD or VAR
  871.               SELECT 1
  872.             ENDIF AVG, STD or VAR
  873. ** End of additions JP
  874.           CASE p_calc = "CNT"
  875.             REPLACE crosstab->&lc_colname WITH ;
  876.                     1 + crosstab->&lc_colname
  877.           CASE p_calc = "MAX"
  878.             REPLACE crosstab->&lc_colname WITH ;
  879.                     MAX( &p_calfld, crosstab->&lc_colname )
  880.           CASE p_calc = "MIN"
  881.             IF crosstab->&lc_colname = 0
  882.               REPLACE crosstab->&lc_colname WITH &p_calfld
  883.             ELSE
  884.               REPLACE crosstab->&lc_colname WITH ;
  885.                       MIN( &p_calfld, crosstab->&lc_colname )
  886.             ENDIF
  887.         ENDCASE
  888.       ENDIF
  889.  
  890.     ENDIF
  891.  
  892.   ENDSCAN
  893.  
  894. ** Next long section added by JP
  895.  
  896. ** Do row and column summaries.
  897. ** In the same pass, use the extra tables to do AVG,STD and VAR calculations
  898. ** and place them in the Crosstab file.
  899.  
  900. ** If you don't want summaries, remove this section
  901.   DECLARE ln_tarray [ln_columns + 2]     && a variable per column of data
  902.   IF p_calc $ "AVG,STD,VAR"              && including summary and unused side
  903.     DECLARE ln_narray[ln_columns + 2]    && column for each table
  904.     IF p_calc # "AVG"
  905.       DECLARE ln_sqarray[ln_columns + 2]
  906.     ENDIF
  907.   ENDIF
  908.   ln_col = 2                         && first column is side field names
  909.   DO WHILE ln_col <= ln_columns + 2  && initialize summary column too
  910.     STORE 0 TO ln_tarray[ ln_col ]   && initialize at 0
  911.     IF p_calc $ "AVG,STD,VAR"
  912.       STORE 0 TO ln_narray[ ln_col ]
  913.       IF p_calc # "AVG"
  914.         STORE 0 TO ln_sqarray[ ln_col ]
  915.       ENDIF
  916.     ENDIF
  917.     ln_col = ln_col + 1
  918.   ENDDO
  919. ** end of part that may be removed if removing summaries
  920.  
  921.   SELECT crosstab
  922.   SCAN                               && for each record
  923.  
  924. ** take out all this if removing summaries
  925.     IF recno() = reccount()          && except the last, which is totals
  926.       EXIT
  927.     ENDIF
  928. ** end of removable part
  929.  
  930.     ln_col = 2
  931.     DO WHILE ln_col <= ln_columns + 1 && for each field except summary
  932.       lc_col = field( ln_col )
  933.  
  934. ** remove next part too for no summaries
  935.       DO CASE
  936.         CASE p_calc $ "CNT,SUM"
  937.           ln_tarray[ ln_col ] = ln_tarray[ ln_col ] + &lc_col
  938.           ln_tarray[ ln_columns + 2 ] = ln_tarray[ ln_columns + 2 ] + &lc_col
  939.         CASE p_calc = "MAX"
  940.           ln_tarray[ ln_col ] = max( ln_tarray[ ln_col ], &lc_col )
  941.           ln_tarray[ ln_columns + 2 ] = ;
  942.               max( ln_tarray[ ln_columns + 2 ], &lc_col )
  943.         CASE p_calc = "MIN"
  944.           ln_tarray[ ln_col ] = min( ln_tarray[ ln_col ], &lc_col )
  945.           ln_tarray[ ln_columns + 2 ] = ;
  946.               min( ln_tarray[ ln_columns + 2 ], &lc_col )
  947.         OTHERWISE
  948. ** to here
  949.           SELECT Number
  950.           GO recno( "crosstab" )
  951.           ln_num = &lc_col
  952.           SELECT crosstab
  953.           IF ln_num = 0
  954.             REPLACE &lc_col WITH 0     && and don't count it
  955.           ELSE
  956. ** and remove from here
  957.             ln_tarray[ ln_col ] = ln_tarray[ ln_col ] + &lc_col
  958.             ln_tarray[ ln_columns + 2 ] = ln_tarray[ ln_columns + 2 ] + &lc_col
  959.             ln_narray[ ln_col ] = ln_narray[ ln_col ] + ln_num
  960.             ln_narray[ ln_columns + 2 ] = ln_narray[ ln_columns + 2 ] + ln_num
  961. ** to here
  962.             IF p_calc = "AVG"
  963.               REPLACE &lc_col WITH &lc_col / ln_num
  964.             ELSE
  965.               SELECT Squares
  966.               GO recno( "crosstab" )
  967.               ln_sq = &lc_col
  968.               SELECT crosstab
  969. ** and next two
  970.               ln_sqarray[ ln_col ] = ln_sqarray[ ln_col ] + ln_sq
  971.               ln_sqarray[ ln_columns + 2 ] = ;
  972.                 ln_sqarray[ ln_columns + 2 ] + ln_sq
  973. ** down to here
  974.               ln_var = ( ln_sq - &lc_col * &lc_col / ln_num ) / ln_num
  975.               REPLACE &lc_col WITH iif( p_calc = "VAR", ln_var, sqrt( ln_var ) )
  976.             ENDIF STD or VAR
  977.           ENDIF zero occurrences
  978. ** and there won't be a case if no summaries so take out next line
  979.       ENDCASE
  980.  
  981.       ln_col = ln_col + 1
  982.     ENDDO
  983.  
  984. ** this all goes out too for no summaries
  985.     IF p_calc $ "AVG,STD,VAR"
  986.       SELECT Number
  987.       GO recno( "crosstab" )
  988.       REPLACE &lc_rtcol WITH ln_narray[ ln_columns + 2 ]
  989.       SELECT crosstab
  990.       IF ln_narray[ ln_columns + 2 ] = 0
  991.         REPLACE &lc_rtcol WITH 0
  992.       ELSE
  993.         IF p_calc = "AVG"
  994.           REPLACE &lc_rtcol WITH ln_tarray[ ln_columns + 2 ] ;
  995.              / ln_narray[ ln_columns + 2 ]
  996.         ELSE
  997.           SELECT Squares
  998.           GO recno( "crosstab" )
  999.           REPLACE &lc_rtcol WITH ln_sqarray[ ln_columns + 2 ]
  1000.           SELECT crosstab
  1001.           ln_var = ( ln_sqarray[ ln_columns + 2 ] -  ;
  1002.                      ln_tarray[ ln_columns + 2 ] *   ;
  1003.                      ln_tarray[ ln_columns + 2 ] /   ;
  1004.                      ln_narray[ ln_columns + 2 ] ) / ;
  1005.                      ln_narray[ ln_columns + 2 ]
  1006.           REPLACE &lc_rtcol WITH iif(p_calc = "VAR", ln_var, sqrt( ln_var ))
  1007.           ln_sqarray[ ln_columns + 2 ] = 0
  1008.         ENDIF AVG
  1009.       ENDIF 0 or not
  1010.       ln_narray[ ln_columns + 2] = 0
  1011.     ELSE
  1012.       REPLACE &lc_rtcol WITH ln_tarray[ ln_columns + 2 ]
  1013.     ENDIF AVG, STD or VAR
  1014.     ln_tarray[ ln_columns + 2 ] = 0
  1015. ** next line has to stay in
  1016.   ENDSCAN
  1017.  
  1018. ** Already at bottom record of crosstab - record column "totals"
  1019. ** take out all the rest of the added routine for no summaries
  1020.  
  1021.   ln_col = 2
  1022.   DO WHILE ln_col <= ln_columns + 1
  1023.     lc_col = field( ln_col )
  1024.     IF p_calc $ "SUM,CNT,MAX,MIN"
  1025.       REPLACE &lc_col WITH ln_tarray[ ln_col ]
  1026.       DO CASE
  1027.         CASE p_calc $ "SUM,CNT"
  1028.           ln_tarray[ ln_columns + 2 ] = ;
  1029.             ln_tarray[ ln_columns + 2 ] + ln_tarray[ ln_col ]
  1030.         CASE p_calc = "MAX"
  1031.           ln_tarray[ ln_columns + 2 ] = ;
  1032.             max( ln_tarray[ ln_columns + 2 ], ln_tarray[ ln_col ] )
  1033.         OTHERWISE
  1034.           ln_tarray[ ln_columns + 2 ] = ;
  1035.             min( ln_tarray[ ln_columns + 2 ], ln_tarray[ ln_col ] )
  1036.       ENDCASE
  1037.     ELSE
  1038.       IF ln_narray[ ln_col ] = 0
  1039.         REPLACE &lc_col WITH 0
  1040.       ELSE
  1041.         ln_tarray[ ln_columns + 2 ] = ;
  1042.           ln_tarray[ ln_columns + 2 ] + ln_tarray[ ln_col ]
  1043.         SELECT Number
  1044.         GO recno( "crosstab" )
  1045.         REPLACE &lc_col WITH ln_narray[ ln_col ]
  1046.         ln_narray[ ln_columns + 2 ] = ;
  1047.           ln_narray[ ln_columns + 2 ] + ln_narray[ ln_col ]
  1048.         IF p_calc = "AVG"
  1049.           SELECT crosstab
  1050.           REPLACE &lc_col WITH ln_tarray[ ln_col ] / ln_narray[ ln_col ]
  1051.         ELSE
  1052.           SELECT Squares
  1053.           GO recno( "crosstab" )
  1054.           REPLACE &lc_col WITH ln_sqarray[ ln_col ]
  1055.           ln_sqarray[ ln_columns + 2 ] = ;
  1056.             ln_sqarray[ ln_columns + 2 ] + ln_sqarray[ ln_col ]
  1057.           ln_var = ( ln_sqarray[ ln_col ] - ;
  1058.                      ln_tarray[ ln_col ] *   ;
  1059.                      ln_tarray[ ln_col ] /   ;
  1060.                      ln_narray[ ln_col ] ) / ;
  1061.                      ln_narray[ ln_col ]
  1062.           SELECT crosstab
  1063.           REPLACE &lc_col WITH iif( p_calc = "VAR", ln_var, sqrt( ln_var ) )
  1064.         ENDIF not AVG
  1065.       ENDIF zero occurrences
  1066.     ENDIF easy operations
  1067.     ln_col = ln_col + 1
  1068.   ENDDO
  1069.  
  1070. * Grand "total"
  1071.   DO CASE
  1072.     CASE p_calc $ "SUM,CNT,MAX,MIN"
  1073.       REPLACE &lc_rtcol WITH ln_tarray[ ln_columns + 2 ]
  1074.     CASE p_calc = "AVG"
  1075.       REPLACE &lc_rtcol WITH ln_tarray[ ln_columns + 2 ] / ;
  1076.                              ln_narray[ ln_columns + 2 ]
  1077.     OTHERWISE
  1078.       ln_var = ( ln_sqarray[ ln_columns + 2 ] -  ;
  1079.                  ln_tarray[ ln_columns + 2 ] *   ;
  1080.                  ln_tarray[ ln_columns + 2 ] /   ;
  1081.                  ln_narray[ ln_columns + 2 ] ) / ;
  1082.                  ln_narray[ ln_columns + 2 ]
  1083.       REPLACE &lc_rtcol WITH iif(p_calc = "VAR", ln_var, sqrt(ln_var))
  1084.   ENDCASE
  1085.   INDEX ON Seq TAG Seq
  1086.   GO BOTTOM
  1087.   REPLACE &lc_psidfld WITH "Summary"
  1088.   SELECT 1
  1089.  
  1090. ** End of long section added by JP; end that can be removed for no summaries.
  1091.  
  1092.   @ 5, 50 SAY "Complete"
  1093.   @ 6, 5 SAY lc_stat5 + + CHR(205) + CHR(205) + CHR(16)
  1094.   @ 6, 50 SAY "Working"
  1095.  
  1096. *-- Remove the UNIQUE Tags on the data file
  1097.   DELETE TAG topfld
  1098.   DELETE TAG sidfld
  1099.  
  1100. *-- Put the CROSSTAB and columns into view
  1101.   CLOSE DATABASE
  1102.   SELECT 1
  1103.   USE crosstab ORDER TAG Seq
  1104.   USE columns IN 2 ORDER TAG column
  1105.  
  1106. *-- Create the field column labels
  1107.   SET FIELDS TO &lc_psidfld. /R
  1108.   SET FIELDS OFF
  1109.   ln_col = 1
  1110.   DO WHILE ln_col <= ln_columns
  1111.     lc_colname = "COL" + SUBSTR( STR( 100 + ln_col, 3, 0), 2, 2 )
  1112.     lc_coltitl = LOOKUP( columns->&lc_ptopfld, lc_colname, columns->column )
  1113.     lc_coltitl = XT_ClnTl( (lc_coltitl) )
  1114.     SET FIELDS TO &lc_coltitl = ( &lc_colname )
  1115.     SET FIELDS OFF
  1116.     ln_col = ln_col + 1
  1117.   ENDDO
  1118.  
  1119. **  Name the field for the row totals; last change by JP; remove for no summary
  1120.   SET FIELDS TO Summary = &lc_rtcol
  1121.  
  1122.   SET FIELDS ON
  1123.  
  1124.   @ 6, 50 SAY "Complete"
  1125.   DO XT_RestEv
  1126. RETURN
  1127. *-- EOP: Com_XTab
  1128.  
  1129.